//
//  rcshell_call.c
//  rcshell
//
//  Created by luolijun on 2021/11/26.
//  Copyright © 2021 com.gitee.newheels. All rights reserved.
//
#include "rcshell_call.h"

#import <SocketRocket/SocketRocket.h>
#import <Foundation/Foundation.h>
#import <objc/message.h>

#include <unistd.h>
#include <assert.h>

#ifndef ASSERT
    #if !defined(NDEBUG)
        #define ASSERT(expr) assert(expr)
    #else
        #define ASSERT(expr)
    #endif
#endif

//  通过对象名和函数名，调用函数
extern NSObject *rcshell_call(NSString *objname, NSString *funcname, NSArray *params, NSError **error);

@interface RCSHELLClientDefault : NSObject <SRWebSocketDelegate>
- (instancetype)initWithAddr:(NSString *)addr hostname:(NSString *)name;

- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessageWithString:(NSString *)string;

- (void)webSocketDidOpen:(SRWebSocket *)webSocket;

- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error;

- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(nullable NSString *)reason wasClean:(BOOL)wasClean;

@end;


//  声明一个错误域
static NSErrorDomain RCSHELL_ERROR_DOMAIN = @"com.baidu.rcshell.ErrorDomain";

//  全局对象 map[string]*rcshll_object
static NSDictionary *globalObjects = nil;

static SRWebSocket *globalWebSocket = nil;

//  本服务的地址
static NSString *globalAddress = nil;

//  本服务对外的唯一名称
static NSString *globalName = nil;

static RCSHELLClientDefault *globalClient = nil;


@implementation RCSHELLClientDefault {
    SRWebSocket *webSocket;
}

- (instancetype)initWithAddr:(NSString *)addr hostname:(NSString *)name {
    if (self = [super init]) {
        NSString *connAddr = [@"" stringByAppendingFormat:@"ws://%@/%@/%@", addr, @"shell", name];
        NSLog(@"Connect: %@", connAddr);

        //  创建urlreq对象
        NSURLRequest *reqAddr = [[NSURLRequest alloc] initWithURL:[[NSURL alloc] initWithString:connAddr]];
        if (reqAddr == nil) {
            NSLog(@"Create reqAddr failed");
            return nil;
        }

        //  用urlreq指向的地址建立websocket
        webSocket = [[SRWebSocket alloc] initWithURLRequest:reqAddr];
        if (webSocket == nil) {
            NSLog(@"Create webSocket failed");
            return nil;
        }

        webSocket.delegate = self;

        [webSocket open];
    }
    return self;
}

//* REQUEST
//
//```text
//        POST /runner/{shell-name}/{object}/{func}
//{
//    "id": "",     //  本次调用的唯一请求id
//    "object": "", //  被调用的对象的名字
//    "func": "",   //  被调用的对象的函数名称
//    "args": []    //  调用参数
//}
//
//```
//
//* REPONSE
//
//```text
//{
//    "id": "",     //  本次调用的唯一请求id
//    "object": "", //  被调用的对象的名字
//    "func": "",   //  被调用的对象的函数名称
//    "result": {}  //  返回的对象
//    "error": {
//        "id": 123,     // 错误码
//                "message": ""  // 错误描述
//    },
//}
//```
- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessageWithString:(NSString *)string {
    NSLog(@"didReceiveMessageWithString: %@", string);

    NSMutableDictionary *reponse = [[NSMutableDictionary alloc] init];

    NSData *reqData = [string dataUsingEncoding:NSUTF8StringEncoding];

    NSError *error = nil;
    NSDictionary *request = [NSJSONSerialization JSONObjectWithData:reqData options:0 error:&error];
    if (request == nil) {
        reponse[@"error"] = error;
        NSLog(@"Convert json failed:%@", error);
    } else {
        NSString *id = request[@"id"];
        NSString *object = request[@"object"];
        NSString *func = request[@"func"];
        NSArray *params = request[@"args"];

        NSObject *result = nil;
        @try {
            result = rcshell_call(object, func, params, &error);
        } @catch (NSException *exp) {
            NSMutableDictionary *info = [NSMutableDictionary dictionary];
            [info setValue:exp.name forKey:@"ExceptionName"];
            [info setValue:exp.reason forKey:@"ExceptionReason"];
            [info setValue:exp.callStackReturnAddresses forKey:@"ExceptionCallStackReturnAddresses"];
            [info setValue:exp.callStackSymbols forKey:@"ExceptionCallStackSymbols"];
            [info setValue:exp.userInfo forKey:@"ExceptionUserInfo"];
            error = [[NSError alloc] initWithDomain:RCSHELL_ERROR_DOMAIN code:-1 userInfo:info];
        }

        reponse[@"id"] = id;
        reponse[@"object"] = object;
        reponse[@"func"] = func;
        if (result == nil) {
            reponse[@"error"] = error;
            NSLog(@"Call failed:%@", error);
        } else {
            reponse[@"result"] = result;
            NSLog(@"Call success:%@", result);
        }
    }

    NSData *repData = [NSJSONSerialization dataWithJSONObject:reponse options:0 error:nil];
    NSString *repString = [[NSString alloc] initWithData:repData encoding:NSUTF8StringEncoding];

    if (![webSocket sendString:repString error:&error]) {
        NSLog(@"Send reponse failed:%@", error);
    }
}

- (void)webSocketDidOpen:(SRWebSocket *)webSocket {
    NSLog(@"[%@] webSocketDidOpen", webSocket.url);
}

- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error {
    NSLog(@"[%@] didFailWithError: %@", webSocket.url, error);
}

- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(nullable NSString *)reason wasClean:(BOOL)wasClean {
    NSLog(@"[%@] didFailWithError: %li %@", webSocket.url, code, reason);
}

@end;

//  初始化
extern int rcshell_init(NSString *addr, NSDictionary *objs) {
    @autoreleasepool {
        //  保存对象表
        globalObjects = objs;

        //  保存地址
        globalAddress = addr;

        //  获取主机名
        char hostname[255 + 1] = {0};
        int ret = gethostname(hostname, sizeof(hostname));
        ASSERT(ret == 0);
        globalName = [[NSString alloc] initWithUTF8String:hostname];

        //  初始化一个客户端
        globalClient = [[RCSHELLClientDefault alloc] initWithAddr:addr hostname:globalName];
    }

    return 0;
}

//  销毁
extern void rcshell_destroy() {

}

//  生成一个 error 对象
extern NSError *rcshell_error(int code, NSString *text) {
    @autoreleasepool {
        return [NSError errorWithDomain:RCSHELL_ERROR_DOMAIN
                                   code:(NSInteger) (code)
                               userInfo:[[NSDictionary alloc] initWithObjects:@[@"desc"] forKeys:@[@""]]
        ];
    }
}

////  注册对象
//extern void rcshell_register(NSString *objname, NSObject *object) {
//    @autoreleasepool {
//        if (globalObjects == nil) {
//            globalObjects = [[NSMutableDictionary alloc] init];
//        }
//
//        [globalObjects setObject:object forKey:objname];
//    }
//}

// clang-format off
typedef NSObject *(*RCFUNC_R1_P0 )(id, SEL);

typedef NSObject *(*RCFUNC_R1_P1 )(id, SEL, id);

typedef NSObject *(*RCFUNC_R1_P2 )(id, SEL, id, id);

typedef NSObject *(*RCFUNC_R1_P3 )(id, SEL, id, id, id);

typedef NSObject *(*RCFUNC_R1_P4 )(id, SEL, id, id, id, id);

typedef NSObject *(*RCFUNC_R1_P5 )(id, SEL, id, id, id, id, id);

typedef NSObject *(*RCFUNC_R1_P6 )(id, SEL, id, id, id, id, id, id);

typedef NSObject *(*RCFUNC_R1_P7 )(id, SEL, id, id, id, id, id, id, id);

typedef NSObject *(*RCFUNC_R1_P8 )(id, SEL, id, id, id, id, id, id, id, id);

typedef NSObject *(*RCFUNC_R1_P9 )(id, SEL, id, id, id, id, id, id, id, id, id);

typedef NSObject *(*RCFUNC_R1_P10)(id, SEL, id, id, id, id, id, id, id, id, id, id);

typedef NSObject *(*RCFUNC_R1_P11)(id, SEL, id, id, id, id, id, id, id, id, id, id, id);

typedef NSObject *(*RCFUNC_R1_P12)(id, SEL, id, id, id, id, id, id, id, id, id, id, id, id);

typedef NSObject *(*RCFUNC_R1_P13)(id, SEL, id, id, id, id, id, id, id, id, id, id, id, id, id);

typedef NSObject *(*RCFUNC_R1_P14)(id, SEL, id, id, id, id, id, id, id, id, id, id, id, id, id, id);

typedef NSObject *(*RCFUNC_R1_P15)(id, SEL, id, id, id, id, id, id, id, id, id, id, id, id, id, id, id);

typedef NSObject *(*RCFUNC_R1_P16)(id, SEL, id, id, id, id, id, id, id, id, id, id, id, id, id, id, id, id);

typedef NSObject *(*RCFUNC_R1_P17)(id, SEL, id, id, id, id, id, id, id, id, id, id, id, id, id, id, id, id, id);

typedef NSObject *(*RCFUNC_R1_P18)(id, SEL, id, id, id, id, id, id, id, id, id, id, id, id, id, id, id, id, id, id);

typedef NSObject *(*RCFUNC_R1_P19)(id, SEL, id, id, id, id, id, id, id, id, id, id, id, id, id, id, id, id, id, id, id);
// clang-format on

//  统一的调用入口函数
extern NSObject *rcshell_call(NSString *objname, NSString *funcname, NSArray *params, NSError **error) {
    @autoreleasepool {

        id object = globalObjects[objname];
        if (object == nil) {
            *error = rcshell_error(-1, [NSString stringWithFormat:@"Can not found object %@", objname]);
            return nil;
        }

        // clang-format off
        switch ([params count]) {
            case 0:
                return ((RCFUNC_R1_P0) objc_msgSend)(object, sel_registerName(funcname.UTF8String));
            case 1:
                return ((RCFUNC_R1_P1) objc_msgSend)(object, sel_registerName(funcname.UTF8String), params[0]);
            case 2:
                return ((RCFUNC_R1_P2) objc_msgSend)(object, sel_registerName(funcname.UTF8String), params[0], params[1]);
            case 3:
                return ((RCFUNC_R1_P3) objc_msgSend)(object, sel_registerName(funcname.UTF8String), params[0], params[1], params[2]);
            case 4:
                return ((RCFUNC_R1_P4) objc_msgSend)(object, sel_registerName(funcname.UTF8String), params[0], params[1], params[2], params[3]);
            case 5:
                return ((RCFUNC_R1_P5) objc_msgSend)(object, sel_registerName(funcname.UTF8String), params[0], params[1], params[2], params[3], params[4]);
            case 6:
                return ((RCFUNC_R1_P6) objc_msgSend)(object, sel_registerName(funcname.UTF8String), params[0], params[1], params[2], params[3], params[4], params[5]);
            case 7:
                return ((RCFUNC_R1_P7) objc_msgSend)(object, sel_registerName(funcname.UTF8String), params[0], params[1], params[2], params[3], params[4], params[5], params[6]);
            case 8:
                return ((RCFUNC_R1_P8) objc_msgSend)(object, sel_registerName(funcname.UTF8String), params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7]);
            case 9:
                return ((RCFUNC_R1_P9) objc_msgSend)(object, sel_registerName(funcname.UTF8String), params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7], params[8]);
            case 10:
                return ((RCFUNC_R1_P10) objc_msgSend)(object, sel_registerName(funcname.UTF8String), params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7], params[8], params[9]);
            case 11:
                return ((RCFUNC_R1_P11) objc_msgSend)(object, sel_registerName(funcname.UTF8String), params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7], params[8], params[9], params[10]);
            case 12:
                return ((RCFUNC_R1_P12) objc_msgSend)(object, sel_registerName(funcname.UTF8String), params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7], params[8], params[9], params[10], params[11]);
            case 13:
                return ((RCFUNC_R1_P13) objc_msgSend)(object, sel_registerName(funcname.UTF8String), params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7], params[8], params[9], params[10], params[11], params[12]);
            case 14:
                return ((RCFUNC_R1_P14) objc_msgSend)(object, sel_registerName(funcname.UTF8String), params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7], params[8], params[9], params[10], params[11], params[12], params[13]);
            case 15:
                return ((RCFUNC_R1_P15) objc_msgSend)(object, sel_registerName(funcname.UTF8String), params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7], params[8], params[9], params[10], params[11], params[12], params[13], params[14]);
            case 16:
                return ((RCFUNC_R1_P16) objc_msgSend)(object, sel_registerName(funcname.UTF8String), params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7], params[8], params[9], params[10], params[11], params[12], params[13], params[14], params[15]);
            case 17:
                return ((RCFUNC_R1_P17) objc_msgSend)(object, sel_registerName(funcname.UTF8String), params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7], params[8], params[9], params[10], params[11], params[12], params[13], params[14], params[15], params[16]);
            case 18:
                return ((RCFUNC_R1_P18) objc_msgSend)(object, sel_registerName(funcname.UTF8String), params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7], params[8], params[9], params[10], params[11], params[12], params[13], params[14], params[15], params[16], params[17]);
            case 19:
                return ((RCFUNC_R1_P19) objc_msgSend)(object, sel_registerName(funcname.UTF8String), params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7], params[8], params[9], params[10], params[11], params[12], params[13], params[14], params[15], params[16], params[17], params[18]);
            default:
                *error = rcshell_error(-1, [NSString stringWithFormat:@"Too many parameters(%lu) %@", (unsigned long) [params count], objname]);
                return nil;
        }
        // clang-format on
    }
}



